package concurrecy.lock2;

import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.*;

/**
 * 读写锁
 */
class JannaReadWriteLock implements ReadWriteLock {
    // 锁状态（高8位为写锁状态，低8位位读锁状态）
    private AtomicInteger state = new AtomicInteger(0);
    // 持有（写）锁的线程
    private Thread owner;
    // 等待队列
    private LinkedBlockingQueue<WaiterThread> waiters = new LinkedBlockingQueue<>();

    /**
     * 尝试获取写锁
     * @return
     */
    private boolean tryLock() {
        int s = state.get();
        int r = s & 0xffff, w = s >>> 16;
        int next = (w + 1) << 16 | r;
        // 如果读锁和写锁都没有被占用，
        if(s == 0 && state.compareAndSet(s, next)) {
            owner = Thread.currentThread();
            return true;
        } else {
            if(owner == Thread.currentThread()) {
                state.set(next);
                return true;
            }
        }
        return false;
    }

    /**
     * 获取写锁
     */
    private void lock() {
        if(!tryLock()) {
            WaiterThread currentWaiterThread = new WaiterThread(1, Thread.currentThread());
            waiters.add(currentWaiterThread); // 把当前线程放入队列中去
            for(;;) {
                if(tryLock()) {
                    waiters.remove(currentWaiterThread);
                    return;
                }
                LockSupport.park();
            }
        }
    }

    /**
     * 尝试释放写锁
     */
    private boolean tryUnlock() {
        if(Thread.currentThread() != owner)
            throw new IllegalMonitorStateException("owner: " + owner);
        int s = state.get();
        int r = s & 0xffff, w = s >>> 16;
        int next = w - 1;
        // 是否已释放
        boolean released = false;
        if(next == 0) {
            owner = null;
            released = true;
        }
        state.set(next << 16 | r);
        return released;
    }

    /**
     * 释放写锁
     */
    private void unlock() {
        if(tryUnlock()) {
//            System.out.println("lock released");
            Iterator<WaiterThread> iterator = waiters.iterator();
            WaiterThread head = waiters.peek();
            // 唤醒队列头
            if(head != null && head.thread != null) {
//                System.out.println("1 wake up " + head.thread.getName());
                LockSupport.unpark(head.thread);
            }
            while(iterator.hasNext()) {
                WaiterThread next = iterator.next();
                if(next == head) // 队列头已经唤醒过了。不用再次唤醒
                    continue;
                // 唤醒所有的读线程
                if(next != null && next.thread != null && next.type == 0) {
//                    System.out.println("1 wake up " + next.thread.getName());
                    LockSupport.unpark(next.thread);
                }
            }
        }
    }

    /**
     * 尝试获取共享锁
     * @return
     */
    private boolean trySharedLock() {
        int s, w, r, next;
        do {
            s = state.get();
            w = s >>> 16;
            if(w != 0)
                return false;
            r = s & 0xffff;
            next = w << 16 | (r + 1);
        } while(!state.compareAndSet(s, next));
//        System.out.println("state -> " + next);
        return true;
    }

    /**
     * 获取共享锁
     */
    private void sharedLock() {
        if(!trySharedLock()) {
            WaiterThread currentWaiterThread = new WaiterThread(0, Thread.currentThread());
            waiters.add(currentWaiterThread); // 把当前线程放入队列中去
            for(;;) {
                if(trySharedLock()) {
                    waiters.remove(currentWaiterThread);
                    return;
                }
                LockSupport.park();
            }
        }
    }

    /**
     * 尝试释放共享锁
     * @return
     */
    private boolean trySharedUnlock() {
        int s, r, w, next;
        do {
            s = state.get();
            r = s & 0xffff;
            w = s >>> 16;
            next = r - 1;
        } while(!state.compareAndSet(s, w << 16 | (r - 1)));
//        System.out.println("state1 -> " + next);
        return next == 0;
    }

    /**
     * 释放共享锁
     */
    private void sharedUnlock() {
        if(trySharedUnlock()) {
//            System.out.println("shared lock released");
            WaiterThread head = waiters.peek();
            if(head != null && head.thread != null) {
//                System.out.println("2 wake up " + head.thread.getName());
                LockSupport.unpark(head.thread);
            }
        }
    }

    @Override
    public Lock readLock() {
        return new Lock() {
            @Override
            public void lock() {
                sharedLock();
            }

            @Override
            public void lockInterruptibly() throws InterruptedException {

            }

            @Override
            public boolean tryLock() {
                return false;
            }

            @Override
            public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
                return false;
            }

            @Override
            public void unlock() {
                sharedUnlock();
            }

            @Override
            public Condition newCondition() {
                return null;
            }
        };
    }

    @Override
    public Lock writeLock() {
        return new Lock() {
            @Override
            public void lock() {
                JannaReadWriteLock.this.lock();
            }

            @Override
            public void lockInterruptibly() throws InterruptedException {

            }

            @Override
            public boolean tryLock() {
                return false;
            }

            @Override
            public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
                return false;
            }

            @Override
            public void unlock() {
                JannaReadWriteLock.this.unlock();
            }

            @Override
            public Condition newCondition() {
                return null;
            }
        };
    }

    private static class WaiterThread {
        int type; // 0 为读线程， 1 为写线程
        Thread thread; // 线程对象
        public WaiterThread(int type, Thread thread) {
            this.type = type;
            this.thread = thread;
        }
    }
}

public class MyReadWriteLockDemo {

    /**
     * 测试多线程获取锁
     * @param lock
     */
    static void test2(Lock lock) {
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(new LockChecker(lock));
        service.execute(new LockChecker(lock));
        service.execute(new LockChecker(lock));
        service.execute(new LockChecker(lock));
        service.shutdown();
    }

    /**
     * 测试ReadWriteLock
     */
    static void test(ReadWriteLock lock) {
        for (int i = 0; i < 2; i++) {
            final int ii = i;
            new Thread(() -> {
                Thread.currentThread().setName("write" + ii);
                System.out.println("write" + ii + " running");
                lock.writeLock().lock();
                try {
                    System.out.println("write" + ii + " get write lock");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.writeLock().unlock();
                    System.out.println("write" + ii + " release write lock");
                }
            }).start();
        }

        for (int i = 0; i < 5; i++) {
            final int ii = i;
            new Thread(() -> {
                Thread.currentThread().setName("read" + ii);
                System.out.println("read" + ii + " running");
                lock.readLock().lock();
                try {
                    System.out.println("read" + ii + " get read lock");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.readLock().unlock();
                    System.out.println("read" + ii + " release read lock");
                }
            }).start();
        }
    }

    public static void main(String[] args) {
        ReadWriteLock lock = new JannaReadWriteLock();
//        ReadWriteLock lock = new ReentrantReadWriteLock();
//        test2(lock.writeLock());
//        test2(lock.readLock());
        test(lock);
    }
}
